package com.slipper.common.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Period;
import java.time.ZoneId;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;

public class DateUtil {
    private static final Logger logger = LoggerFactory.getLogger(DateUtil.class);

    /** 按 yyyy-MM-dd HH:mm:ss 格式化时间. */
    private static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    private static final String DATE_TIME_FORMAT2 = "yyyyMMddHHmmss";
    private static final String FORMAT_YYYYMMDD = "yyyyMMdd";
    private static final String TIME_FORMAT = "HH:mm:ss";
    private static final String DATE_MINUTE_FORMAT = "yyyy-MM-dd HH:mm";
    private static final String HOUR_MINUTE_FORMAT = "HH:mm";
    private static final String YEAR_FORMAT = "yyyy";
    private static final String MONTH_FORMAT = "yyyy-MM";
    private static final String DATE_FORMAT = "yyyy-MM-dd";
    private static final String DATE_CHINA_FORMAT = "yyyy年MM月dd日";
    
    /**
     * 格式化时间.
     * @linkplain #DATE_TIME_FORMAT
     * @param date 要格式化的时间
     * @return 格式化后的字符串
     */
    public static String format(Date date, String format) {
        return new SimpleDateFormat(format).format(date);
    }

    /**
     * 格式化时间.
     * @linkplain #DATE_MINUTE_FORMAT
     * @param date 要格式化的时间
     * @return 格式化后的字符串
     */
    public static String formatDateMinute(Date date) {
        return date == null ? "" : DateUtil.format(date, DATE_MINUTE_FORMAT);
    }

    /**
     * 格式化时间  yyyy-MM-dd HH:mm
     * @linkplain #DATE_MINUTE_FORMAT
     * @param date 要格式化的时间
     * @return 格式化后的字符串
     */
    public static String formatHourMinute(Date date) {
        return date == null ? "" : DateUtil.format(date, HOUR_MINUTE_FORMAT);
    }

    /**
     * 格式化时间  yyyy-MM-dd HH:mm:ss
     * @linkplain #DATE_TIME_FORMAT
     * @param date 要格式化的时间
     * @return 格式化后的字符串
     */
    public static String formatTime(Date date) {
        return date == null ? "" : DateUtil.format(date, DATE_TIME_FORMAT);
    }

    /**
     * 格式化时间.
     * @linkplain #DATE_TIME_FORMAT
     * @return 格式化后的字符串
     */
    public static String formatTime(String dateStr) throws ParseException {
        if (dateStr == null || dateStr.isEmpty()) {
            return "";
        }
        Date date = DateUtil.getDateFormat(dateStr.length()).parse(dateStr);
        return date == null ? "" : DateUtil.format(date, DATE_TIME_FORMAT);
    }

    /**
     * 格式化时间.
     * @linkplain #DATE_TIME_FORMAT
     * @return 格式化后的字符串
     */
    public static Date parseDate(String dateStr) throws ParseException {
        if (dateStr == null || dateStr.isEmpty()) {
            return null;
        }
        return DateUtil.getDateFormat(dateStr.length()).parse(dateStr);
    }
    
    /**
     * 格式化时间.
     * @linkplain #DATE_TIME_FORMAT
     * @return 格式化后的字符串
     */
    public static Date parseDate(String dateStr, String format) throws ParseException {
        return new SimpleDateFormat(format).parse(dateStr);
    }

    /**
     * 获取当前日期的整数20161010
     */
    public static int getCurrIntDate() {
        return Integer.parseInt(DateUtil.format(new Date(), FORMAT_YYYYMMDD));
    }

    /**
     * 格式化日期 yyyy-MM-dd
     * @param date 要格式化的时间
     * @return 格式化后的字符串
     */
    public static String formatDate(Date date) {
        return date == null ? "" : DateUtil.format(date, DATE_FORMAT);
    }

    /**
     * 格式化日期 yyyy-MM-dd.
     * @param date 要格式化的时间
     * @return 格式化后的字符串
     */
    public static String formatDate(java.sql.Date date) {
        return date == null ? "" : DateUtil.format(date, DATE_FORMAT);
    }

    /**
     * 格式化日期.
     * @param date 要格式化的时间
     * @return 格式化后的字符串
     */
    public static String formatYear(Date date) {
        return date == null ? "" : DateUtil.format(date, YEAR_FORMAT);
    }

    /**
     * 格式化日期.
     * @param date 要格式化的时间
     * @return 格式化后的字符串
     */
    public static String formatMonth(Date date) {
        return date == null ? "" : DateUtil.format(date, MONTH_FORMAT);
    }

    /**
     * 格式化日期.
     * @param date 要格式化的时间
     * @param format 格式化对象
     * @return 格式化后的字符串
     */
    public static String formatDate(Date date, SimpleDateFormat format) {
        return date == null ? "" : format.format(date);
    }

    /**
     * 格式化当前日期yyyyMMddHHmmss
     * @return 格式化后的字符串
     */
    public static String formatDateNoSplit() {
        return format(new Date(), DATE_TIME_FORMAT2);
    }

    /**
     * 格式化当前日期yyyyMMddHHmmss
     * @param second 正数为当前时间+second，负数为当前时间-second
     * @return 格式化后的字符串
     */
    public static String formatDateNoSplit(int second) {
        return format(getNextTime(new Date(), second), DATE_TIME_FORMAT2);
    }

    /**
     * 格式化日期,将日期格式化为 x年x月x日 形式
     * @param date 要格式化的时间
     * @return 格式化后的字符串
     */
    public static String formatDateChinese(Date date) {
        if (date == null) {
            return "";
        }
        return DateUtil.format(date, DATE_CHINA_FORMAT);
    }

    /**
     * 根据日期得到中文日期
     * @param date 日期
     * @return 中文格式日期
     */
    public static String chineseDate(Date date) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日");
        return dateFormat.format(date);
    }

    /**
     * 根据日期得到中文星期几
     * @param date 日期
     * @return 星期几
     */
    public static String chineseWeekDay(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int weekDay = calendar.get(Calendar.DAY_OF_WEEK);
        String[] weeks = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
        return weeks[weekDay - 1];
    }

    /**
     * 根据日期得到星期几
     * @param date 日期
     * @return 星期几
     */
    public static String weekDay(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int weekDay = calendar.get(Calendar.DAY_OF_WEEK);
        String[] weeks = {"0", "1", "2", "3", "4", "5", "6"};
        return weeks[weekDay - 1];
    }

    /**
     * 根据时间显示长度获取解释对象
     */
    public static SimpleDateFormat getDateFormat(int length) {
        String parseString = "yyyy-MM-dd HH:mm:ss.SSS".substring(0, length);
        return new SimpleDateFormat(parseString);
    }

    /**
     * 格式化时间
     */
    public static String formatOnlyTime(Date date) {
        return format(date, TIME_FORMAT);
    }

    /**
     * 返回从start到end相差的日期，end>start时返回正数
     */
    public static int minuteDiff(Date start, Date end) throws ParseException {
        if (start == null || end == null) {
            return 0;
        }
        start = parse(format(start, DATE_FORMAT), DATE_FORMAT);
        end = parse(format(end, DATE_FORMAT), DATE_FORMAT);
        long mills = end.getTime() - start.getTime();
        return (int) (mills / (60 * 1000));
    }

    /**
     * 计算相差年份
     */
    public static BigDecimal yearDiff(Date start, Date end) {
        Period period = Period.between(
                start.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(),
                end.toInstant().atZone(ZoneId.systemDefault()).toLocalDate());

        return new BigDecimal(period.getYears())
                .add(BigDecimal.valueOf(period.getMonths() * 0.1 / 12))
                .setScale(2, RoundingMode.HALF_UP);
    }

    /**
     * 返回从start到end相差的日期，end>start时返回正数
     */
    public static int dayDiff(Date start, Date end) throws ParseException {
        if (start == null || end == null) {
            return 0;
        }
        start = parse(format(start, DATE_FORMAT), DATE_FORMAT);
        end = parse(format(end, DATE_FORMAT), DATE_FORMAT);
        long mills = end.getTime() - start.getTime();
        return (int) (mills / (24 * 60 * 60 * 1000));
    }

    /**
     * 判断所给日期是否为在本周
     * @param timeMillis 日期时间
     */
    public static boolean isCurWeek(long timeMillis) {
        try {
            if (DateUtil.getDate(DateUtil.getCurWeekMonday(), "00:00:00").getTime() < timeMillis
                    && timeMillis < DateUtil.getDate(DateUtil.getCurWeekSunday(), "23:59:59").getTime()) {
                return true;
            }
        } catch (ParseException e) {
            logger.error("", e);
        }
        return false;
    }

    /**
     * 获取日期，格式为：2014-10-11 00:00:00
     */
    public static Date getDay(Date date) {
        try {
            return parse(format(date, DATE_FORMAT), DATE_FORMAT);
        } catch (ParseException e) {
            logger.error("", e);
        }
        return date;
    }

    /**
     * 获取当天
     */
    public static Date getToday() {
        return new Date();
    }

    /**
     * 获取second后的时间
     */
    public static Date getNextTime(Date date, int second) {
        Calendar calendar = Calendar.getInstance(Locale.CHINA);
        calendar.setTimeInMillis(date.getTime());
        calendar.add(Calendar.SECOND, second);
        return calendar.getTime();
    }

    /**
     * 获取hour后的时间
     */
    public static Date getNextHour(Date date, int hour) {
        Calendar calendar = Calendar.getInstance(Locale.CHINA);
        calendar.setTimeInMillis(date.getTime());
        calendar.add(Calendar.HOUR_OF_DAY, hour);
        return calendar.getTime();
    }

    /**
     * 获取第二天
     */
    public static Date getNextday(Date date) {
        Calendar calendar = Calendar.getInstance(Locale.CHINA);
        calendar.setTimeInMillis(date.getTime());
        calendar.add(Calendar.DATE, 1);
        return calendar.getTime();
    }

    /**
     * 获取下一个月份
     */
    public static Date getNextMonth(Date date, int month) {
        Calendar calendar = Calendar.getInstance(Locale.CHINA);
        calendar.setTimeInMillis(date.getTime());
        calendar.add(Calendar.MONTH, month);
        return calendar.getTime();
    }

    /**
     * 获取第二天
     */
    public static Date getNextday(Date date, int day) {
        Calendar calendar = Calendar.getInstance(Locale.CHINA);
        calendar.setTimeInMillis(date.getTime());
        calendar.add(Calendar.DATE, day);
        return getDay(calendar.getTime());
    }

    /**
     * 获取第二天
     * @param date yyyy-mm-dd
     */
    public static String getNextday(String date) throws ParseException {
        Calendar calendar = Calendar.getInstance(Locale.CHINA);
        calendar.setTimeInMillis(parseDate(date).getTime());
        calendar.add(Calendar.DATE, 1);
        return format(calendar.getTime(), DATE_FORMAT);
    }

    /**
     * 获取昨天
     */
    public static Date getYesterday() {
        Calendar calendar = Calendar.getInstance(Locale.CHINA);
        calendar.setTime(new Date());
        calendar.add(Calendar.DATE, -1);
        return calendar.getTime();
    }

    /**
     * 获取前天
     */
    public static Date getBeforeYesterday() {
        Calendar calendar = Calendar.getInstance(Locale.CHINA);
        calendar.setTime(new Date());
        calendar.add(Calendar.DATE, -2);
        return calendar.getTime();
    }

    /**
     * 获取时间
     */
    public static Date getDate(Date date, String time) throws ParseException {
        String dateStr = formatDate(date).trim() + " " + time.trim();
        return DateUtil.parseDate(dateStr);
    }

    /**
     * 获取本周星期一
     */
    public static Date getCurWeekMonday() {
        Calendar calendar = Calendar.getInstance(Locale.CHINA);
        calendar.setTimeInMillis(System.currentTimeMillis());
        calendar.setFirstDayOfWeek(Calendar.MONDAY);
        calendar.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);

        Calendar calendar2 = (Calendar) calendar.clone();
        calendar2.clear();
        calendar2.set(Calendar.YEAR, calendar.get(Calendar.YEAR));
        calendar2.set(Calendar.MONTH, calendar.get(Calendar.MONTH));
        calendar2.set(Calendar.DAY_OF_MONTH, calendar.get(Calendar.DAY_OF_MONTH));
        return calendar2.getTime();
    }

    /**
     * 获取本周星期日
     */
    public static Date getCurWeekSunday() {
        Calendar calendar = Calendar.getInstance(Locale.CHINA);
        calendar.setTimeInMillis(System.currentTimeMillis());
        calendar.setFirstDayOfWeek(Calendar.MONDAY);
        calendar.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
        return calendar.getTime();
    }

    /**
     * 获取上周星期一
     */
    public static Date getLastWeekMonday() {
        Calendar calendar = Calendar.getInstance(Locale.CHINA);
        calendar.setTimeInMillis(System.currentTimeMillis());
        calendar.setFirstDayOfWeek(Calendar.MONDAY);
        calendar.add(Calendar.DATE, -7);
        calendar.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);

        Calendar calendar2 = (Calendar) calendar.clone();
        calendar2.clear();
        calendar2.set(Calendar.YEAR, calendar.get(Calendar.YEAR));
        calendar2.set(Calendar.MONTH, calendar.get(Calendar.MONTH));
        calendar2.set(Calendar.DAY_OF_MONTH, calendar.get(Calendar.DAY_OF_MONTH));
        return calendar2.getTime();
    }

    /**
     * 获取上周星期六
     */
    public static Date getLastWeekSaturday() {
        Calendar calendar = Calendar.getInstance(Locale.CHINA);
        calendar.setTimeInMillis(System.currentTimeMillis());
        calendar.setFirstDayOfWeek(Calendar.MONDAY);
        calendar.add(Calendar.DATE, -7);
        calendar.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY);

        Calendar calendar2 = (Calendar) calendar.clone();
        calendar2.clear();
        calendar2.set(Calendar.YEAR, calendar.get(Calendar.YEAR));
        calendar2.set(Calendar.MONTH, calendar.get(Calendar.MONTH));
        calendar2.set(Calendar.DAY_OF_MONTH, calendar.get(Calendar.DAY_OF_MONTH));
        return calendar2.getTime();
    }

    /**
     * 取得给定日期所在年份第一日的日期
     */
    public static Date getYearFirstDate(Date curDate) {
        Calendar calendar = Calendar.getInstance(Locale.CHINA);
        calendar.setTimeInMillis(curDate.getTime());
        Calendar calendar2 = (Calendar) calendar.clone();
        calendar2.clear();
        calendar2.set(Calendar.YEAR, calendar.get(Calendar.YEAR));
        return calendar2.getTime();
    }

    /**
     * 取得给定日期所在月第一日的日期
     */
    public static Date getMonFirstDate(Date curDate) {
        Calendar calendar = Calendar.getInstance(Locale.CHINA);
        calendar.setTimeInMillis(curDate.getTime());
        Calendar calendar2 = (Calendar) calendar.clone();
        calendar2.clear();
        calendar2.set(Calendar.YEAR, calendar.get(Calendar.YEAR));
        calendar2.set(Calendar.MONTH, calendar.get(Calendar.MONTH));
        return calendar2.getTime();
    }

    /**
     * 取得给定日期所在月最后一日的日期
     */
    public static Date getMonLastDate(Date curDate) {
        Calendar calendar = Calendar.getInstance(Locale.CHINA);
        calendar.setTimeInMillis(curDate.getTime());
        Calendar calendar2 = (Calendar) calendar.clone();
        calendar2.clear();
        calendar2.set(Calendar.YEAR, calendar.get(Calendar.YEAR));
        calendar2.set(Calendar.MONTH, calendar.get(Calendar.MONTH) + 1);
        calendar2.add(Calendar.DATE, -1);
        return calendar2.getTime();
    }

    /**
     * 获取上月1号
     */
    public static Date getLastMonthFirstDay(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.add(Calendar.MONTH, -1);
        cal.set(Calendar.DATE, 1);
        return cal.getTime();
    }

    /**
     * 获取上月最后一天
     */
    public static Date getLastMonthLastDay(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.DATE, 1);
        cal.add(Calendar.DATE, -1);
        return cal.getTime();
    }

    /**
     * 获取给定日期所在月份的第几周
     */
    public static int getWeekOfMonth(Date curDate) {
        Calendar calendar = Calendar.getInstance(Locale.CHINA);
        calendar.setTimeInMillis(curDate.getTime());
        return calendar.get(Calendar.WEEK_OF_MONTH);
    }
    
    /**
     * 获取给定日期是星期几
     */
    public static int getCurrIntWeek() {
        return getWeekOfDate(new Date());
    }

    /**
     * 获取给定日期是星期几
     */
    public static int getWeekOfDate(Date curDate) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(curDate);
        int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
        if (w < 0) {
            w = 0;
        }
        return w;
    }

    /**
     * 取得最大的连续日期记数
     */
    public static int getMaxContinuityDay(List<Map<String, Object>> dateList, String dateKey) throws Exception {
        int result = 0;
        String preDay = null;
        for (Map<String, Object> map : dateList) {
            String curDay = (String) map.get(dateKey);
            if (curDay == null) {
                result = 0;
                preDay = null;
            } else {
                if (preDay == null) {
                    result++;
                } else {
                    if (getNextday(preDay).equals(curDay)) {
                        result++;
                    } else {
                        result = 1;
                    }
                }
                preDay = curDay;
            }
        }
        return result;
    }

    /**
     * 获取从startDate到endDate的秒数，startDate<=endDate为正数，否则为负数
     * @param startDate 为null时取当前时间
     * @param endDate 为null时取当前时间
     */
    public static int getDistanceSecond(Date startDate, Date endDate) {
        long startTime;
        long endTime;
        if (startDate == null) {
            startTime = new Date().getTime();
        } else {
            startTime = startDate.getTime();
        }
        if (endDate == null) {
            endTime = new Date().getTime();
        } else {
            endTime = endDate.getTime();
        }
        return (int) ((endTime - startTime) / 1000);
    }

    /**
     * time1 > time2: 1, time1 = time2: 0, time1 < time2: -1
     */
    public static int timeCompare(String time1, String time2) {
        if ((time1 == null || time1.isEmpty())
                && (time2 == null || time2.isEmpty())) {
            return 0;
        }
        if ((time1 == null || time1.isEmpty())) {
            return -1;
        }
        if ((time2 == null || time2.isEmpty())) {
            return 1;
        }
        if (time1.equals(time2)) {
            return 0;
        }

        String[] time1s = time1.split(":");
        String[] time2s = time2.split(":");
        if (time1s.length < 3 || time2s.length < 3) {
            return 0;
        }
        if (Integer.parseInt(time1s[0]) == Integer.parseInt(time2s[0])) {
            if (Integer.parseInt(time1s[1]) == Integer.parseInt(time2s[1])) {
                return Integer.compare(Integer.parseInt(time1s[2]), Integer.parseInt(time2s[2]));
            } else if (Integer.parseInt(time1s[1]) > Integer.parseInt(time2s[1])) {
                return 1;
            } else {
                return -1;
            }
        } else if (Integer.parseInt(time1s[0]) > Integer.parseInt(time2s[0])) {
            return 1;
        } else {
            return -1;
        }
    }

    /**
     * 验证日期字符串是否符合指定的格式
     * @param str 日期字符串
     * @param formatString 指定的格式
     */
    public static boolean isValidDate(String str, String formatString) {
        // 指定日期格式，注意yyyy/MM/dd区分大小写；
        SimpleDateFormat format = new SimpleDateFormat(formatString);
        try {
            // 设置lenient为false.
            // 否则SimpleDateFormat会比较宽松地验证日期，比如2007/02/29会被接受，并转换成2007/03/01
            format.setLenient(false);
            format.parse(str);
        } catch (Exception e) {
            // e.printStackTrace();
            // 如果throw java.text.ParseException或者NullPointerException，就说明格式不对
            return false;
        }
        return true;
    }

    public static Date parse(String str, String format) throws ParseException {
        if (str == null || str.isEmpty()) {
            return null;
        }
        return new SimpleDateFormat(format).parse(str);
    }

}
